/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.imageutils;

import com.facebook.common.internal.Preconditions;
import com.facebook.infer.annotation.SuppressLint;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.PixelFormat;
import ohos.utils.Pair;
import ohos.utils.net.Uri;

import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;

/** This class contains utility method for Bitmap */
public final class BitmapUtil {
  private static final int DECODE_BUFFER_SIZE = 16 * 1024;
  private static final int POOL_SIZE = 12;
  private static final Pools.SynchronizedPool<ByteBuffer> DECODE_BUFFERS =
      new Pools.SynchronizedPool<>(POOL_SIZE);

  /** Bytes per pixel definitions */
  public static final int ALPHA_8_BYTES_PER_PIXEL = 1;

  public static final int ARGB_4444_BYTES_PER_PIXEL = 2;
  public static final int ARGB_8888_BYTES_PER_PIXEL = 4;
  public static final int RGB_565_BYTES_PER_PIXEL = 2;
  public static final int RGBA_F16_BYTES_PER_PIXEL = 8;
  public static final int UNKNOWN_BYTES_PER_PIXEL = 0;

  public static final float MAX_BITMAP_SIZE = 2048f;

  /** @return size in bytes of the underlying bitmap */
  @SuppressLint("NewApi")
  public static int getSizeInBytes(@Nullable PixelMap bitmap) {
    if (bitmap == null) {
      return 0;
    }

    // There's a known issue in KitKat where getAllocationByteCount() can throw an NPE. This was
    // apparently fixed in MR1: http://bit.ly/1IvdRpd. So we do a version check here, and
    // catch any potential NPEs just to be safe.

      try {
        return (int) bitmap.getPixelBytesCapacity();
      } catch (NullPointerException npe) {
        // Swallow exception and try fallbacks.
      }



      return (int) bitmap.getPixelBytesNumber();

  }

  /**
   * Decodes only the bounds of an image and returns its width and height or null if the size can't
   * be determined
   *
   * @param bytes the input byte array of the image
   * @return dimensions of the image
   */
  public static @Nullable Pair<Integer, Integer> decodeDimensions(byte[] bytes) {
    // wrapping with ByteArrayInputStream is cheap and we don't have duplicate implementation
    return decodeDimensions(new ByteArrayInputStream(bytes));
  }

  /**
   * Decodes the bounds of an image from its Uri and returns a pair of the dimensions
   *
   * @param uri the Uri of the image
   * @return dimensions of the image
   */
  public static @Nullable Pair<Integer, Integer> decodeDimensions(Uri uri) {
    Preconditions.checkNotNull(uri);
    ImageSource.SourceOptions opts = new ImageSource.SourceOptions();
    opts.formatHint = "image/png";
    ImageSource imageSource = ImageSource.create(uri.getDecodedPath(),opts);
    ImageInfo imageInfo = imageSource.getImageInfo();


//    BitmapFactory.Options options = new BitmapFactory.Options();
//    options.inJustDecodeBounds = true;
//    BitmapFactory.decodeFile(uri.getPath(), options);
    return (imageInfo.size.width == -1 || imageInfo.size.height == -1)
        ? null
        : new Pair<>(imageInfo.size.width, imageInfo.size.height);
  }

  /**
   * Decodes the bounds of an image and returns its width and height or null if the size can't be
   * determined
   *
   * @param is the InputStream containing the image data
   * @return dimensions of the image
   */
  public static @Nullable Pair<Integer, Integer> decodeDimensions(InputStream is) {
    Preconditions.checkNotNull(is);
    ByteBuffer byteBuffer = DECODE_BUFFERS.acquire();
    if (byteBuffer == null) {
      byteBuffer = ByteBuffer.allocate(DECODE_BUFFER_SIZE);
    }

    try {
      ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
      ImageSource source = ImageSource.create(is,sourceOptions);

      ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
      PixelMap pixelMap = source.createPixelmap(decodingOptions);

      return (pixelMap.getImageInfo().size.width == -1 || pixelMap.getImageInfo().size.height == -1)
              ? null
              : new Pair<>(pixelMap.getImageInfo().size.width, pixelMap.getImageInfo().size.height);
    } finally {
      DECODE_BUFFERS.release(byteBuffer);
    }
  }

  /**
   * Decodes the bounds of an image and returns its width and height or null if the size can't be
   * determined. It also recovers the color space of the image, or null if it can't be determined.
   *
   * @param is the InputStream containing the image data
   * @return the metadata of the image
   */
  public static ImageMetaData decodeDimensionsAndColorSpace(InputStream is) {
    Preconditions.checkNotNull(is);
    ByteBuffer byteBuffer = DECODE_BUFFERS.acquire();
    if (byteBuffer == null) {
      byteBuffer = ByteBuffer.allocate(DECODE_BUFFER_SIZE);
    }
    try {
      ImageSource.SourceOptions opts = new ImageSource.SourceOptions();
      opts.formatHint = "image/png";
      ImageSource imageSource = ImageSource.create(is,opts);
      ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
      PixelMap pixelMap = imageSource.createPixelmap(decodingOptions);
      ImageInfo imageInfo = pixelMap.getImageInfo();
      return new ImageMetaData(imageInfo.size.width, imageInfo.size.height, imageInfo.colorSpace);
    } finally {
      DECODE_BUFFERS.release(byteBuffer);
    }
  }

  /**
   * Returns the amount of bytes used by a pixel in a specific {@link
   * PixelFormat}
   *
   * @param bitmapConfig the {@link PixelFormat} for which the size in byte will
   *     be returned
   * @return
   */
  public static int getPixelSizeForBitmapConfig(PixelFormat bitmapConfig) {

    switch (bitmapConfig) {
      case ARGB_8888:
        return ARGB_8888_BYTES_PER_PIXEL;
//      case ALPHA_8:
//        return ALPHA_8_BYTES_PER_PIXEL;
//      case ARGB_4444:
//        return ARGB_4444_BYTES_PER_PIXEL;
      case RGB_565:
        return RGB_565_BYTES_PER_PIXEL;
      case UNKNOWN:
        return UNKNOWN_BYTES_PER_PIXEL;
//      case RGBA_F16:
//        return RGBA_F16_BYTES_PER_PIXEL;
    }
    throw new UnsupportedOperationException("The provided Bitmap.Config is not supported");
  }

  /**
   * Returns the size in byte of an image with specific size and {@link
   * PixelFormat}
   *
   * @param width the width of the image
   * @param height the height of the image
   * @param bitmapConfig the {@link PixelFormat} for which the size in byte will
   *     be returned
   * @return
   */
  public static int getSizeInByteForBitmap(int width, int height, PixelFormat bitmapConfig) {
    return width * height * getPixelSizeForBitmapConfig(bitmapConfig);
  }


//  public static PixelMap setImageUri(Uri uri){
//    File file = new File(uri.getDecodedPath());
//    FileInputStream fileInputStream = null;
//    try {
//      fileInputStream = new FileInputStream(file);
//      ImageSource.SourceOptions opts = new ImageSource.SourceOptions();
//      opts.formatHint = "image/png";
//      ImageSource imageSource = ImageSource.create(fileInputStream,opts);
//      ImageSource.DecodingOptions options = new ImageSource.DecodingOptions();
//      options.desiredSize = new Size(0,0);
//      options.desiredRegion = new Rect(0,0,0,0);
//      return imageSource.createPixelmap(options);
//    } catch (FileNotFoundException e) {
//      e.printStackTrace();
//    }finally {
//      try {
//        if (fileInputStream != null) {
//          fileInputStream.close();
//        }
//      } catch (IOException e) {
//        e.printStackTrace();
//      }
//    }
//    return null;
//  }
}
